package com.canyou.operlog;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEvent;

import java.util.ArrayList;
import java.util.List;

/**
 * 操作日志记录
 *
 * @author fy
 * @date 20/04/22 13:39
 */
@Slf4j
public final class OpLogUtils {

    private OpLogUtils() {
    }

    private static final ThreadLocal<OpLogEvent> THREAD_LOCAL_OPERATE_LOG = new ThreadLocal<>();

    private static final ThreadLocal<Long> THREAD_LOCAL_TIME_MILLIS = new ThreadLocal<>();

    /**
     * 操作记录的数据标识, 比如如果要记录 房号为xxx的数据
     *
     * @param identification 主数据标识
     */
    public static void logDataId(String identification) {
        OpLogEvent operateLog = get();
        operateLog.setMainDataId(identification);
    }

    /**
     * 添加一段操作记录, 自行写入一段操作文字
     *
     * @param operate 操作记录文字
     */
    public static void logOperate(String operate) {
        OpLogEvent operateLog = get();
        operateLog.getOperateList().add(operate);
    }

    /**
     * 记录操作的模块, 如果不设置, 则默认读取Controller上的@Api的tags属性
     *
     * @param module 所在模块
     */
    public static void logModule(String module) {
        OpLogEvent operateLog = get();
        operateLog.setModule(module);
    }

    /**
     * 模型实体编辑字段修改操作日志
     * 字段名称默认读取DTO属性字段上的@ApiModelProperty的value属性
     *
     * @param source            原数据库模型
     * @param target            修改的模型
     * @param containProperties 要记录的属性名称
     */
    public static void logEdit(Object source, Object target, String... containProperties) {
        List<String> operateList = new ArrayList<>();
        try {
            operateList = BeanUtils.compareProperties(source, target, containProperties);
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
        }
        OpLogEvent operateLog = get();
        operateLog.getOperateList().addAll(operateList);
    }

    /**
     * 不需要外部调用
     *
     * @return
     */
    protected static OpLogEvent getLog() {
        OpLogEvent log = THREAD_LOCAL_OPERATE_LOG.get();
        THREAD_LOCAL_OPERATE_LOG.remove();
        return log;
    }

    private static void setLog(OpLogEvent operateLog) {
        THREAD_LOCAL_OPERATE_LOG.set(operateLog);
    }

    private static OpLogEvent get() {
        OpLogEvent operateLog = THREAD_LOCAL_OPERATE_LOG.get();
        if (operateLog == null) {
            operateLog = new OpLogEvent();
            setLog(operateLog);
        }
        return operateLog;
    }

    @Getter
    @Setter
    @ToString
    @Accessors(chain = true)
    public static class OpLogEvent extends ApplicationEvent {

        public OpLogEvent(Object source) {
            super(source);
        }

        public OpLogEvent() {
            super(new Object());
        }

        private String module;

        private String requestUrl;

        private String requestType;

        private String mainDataId;

        private String requestParam;

        private Integer responseCode;

        private String responseData;

        private String responseMsg;

        private String clientId;

        private String username;

        private String orgShortName;

        private String ipAddr;

        private Long spendTime;

        private List<String> operateList = new ArrayList<>();

    }

    /**
     * 开始计时器并放入当前线程
     */
    static void startStopWatch() {
        THREAD_LOCAL_TIME_MILLIS.set(System.currentTimeMillis());
    }

    /**
     * 从当前线程获取计时器, 获取时间停止并移除
     *
     * @return
     */
    static long getStopWatchTime() {
        try {
            Long lastTimeMillis = THREAD_LOCAL_TIME_MILLIS.get();
            return System.currentTimeMillis() - lastTimeMillis;
        } catch (Exception ex) {
            log.warn(ex.getMessage(), ex);
            THREAD_LOCAL_TIME_MILLIS.remove();
        }
        return 0L;
    }

}
